using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using WeatherSystem.API.Data;
using WeatherSystem.API.Models;

namespace WeatherSystem.API.Services
{
    /// <summary>
    /// 认证服务接口
    /// </summary>
    public interface IAuthService
    {
        Task<LoginResponse?> LoginAsync(LoginRequest request, string ipAddress);
        Task<TokenResponse?> RefreshTokenAsync(string refreshToken, string ipAddress);
        Task<bool> RegisterAsync(RegisterRequest request);
        Task<bool> ChangePasswordAsync(Guid userId, ChangePasswordRequest request);
        Task<bool> RevokeTokenAsync(string refreshToken, string ipAddress);
        Task<bool> RevokeAllTokensAsync(Guid userId);
        Task<UserInfo?> GetUserInfoAsync(Guid userId);
        Task<bool> ValidateTokenAsync(string token);
    }

    /// <summary>
    /// 认证服务实现
    /// </summary>
    public class AuthService : IAuthService
    {
        private readonly WeatherDbContext _context;
        private readonly IConfiguration _configuration;
        private readonly ILogger<AuthService> _logger;
        private readonly IPasswordHasher<User> _passwordHasher;

        // 安全配置
        private readonly int _maxFailedAttempts = 5;
        private readonly TimeSpan _lockoutDuration = TimeSpan.FromMinutes(15);
        private readonly TimeSpan _accessTokenLifetime = TimeSpan.FromHours(1);
        private readonly TimeSpan _refreshTokenLifetime = TimeSpan.FromDays(7);

        public AuthService(
            WeatherDbContext context,
            IConfiguration configuration,
            ILogger<AuthService> logger,
            IPasswordHasher<User> passwordHasher)
        {
            _context = context;
            _configuration = configuration;
            _logger = logger;
            _passwordHasher = passwordHasher;
        }

        /// <summary>
        /// 用户登录
        /// </summary>
        public async Task<LoginResponse?> LoginAsync(LoginRequest request, string ipAddress)
        {
            try
            {
                var user = await _context.Users
                    .FirstOrDefaultAsync(u => u.Username == request.Username && u.IsActive);

                if (user == null)
                {
                    _logger.LogWarning("登录失败：用户名 {Username} 不存在", request.Username);
                    return null;
                }

                // 检查账户锁定状态
                if (IsAccountLocked(user))
                {
                    _logger.LogWarning("登录失败：账户 {Username} 已被锁定", request.Username);
                    return null;
                }

                // 验证密码
                var passwordVerificationResult = _passwordHasher.VerifyHashedPassword(user, user.PasswordHash, request.Password);
                
                if (passwordVerificationResult != PasswordVerificationResult.Success)
                {
                    await HandleFailedLoginAsync(user, ipAddress);
                    _logger.LogWarning("登录失败：用户 {Username} 密码错误", request.Username);
                    return null;
                }

                // 登录成功，重置失败计数
                await HandleSuccessfulLoginAsync(user);

                // 生成令牌
                var accessToken = GenerateAccessToken(user);
                var refreshToken = await GenerateRefreshTokenAsync(user, ipAddress, request.RememberMe);

                var response = new LoginResponse
                {
                    AccessToken = accessToken,
                    RefreshToken = refreshToken.Token,
                    ExpiresAt = DateTime.UtcNow.Add(_accessTokenLifetime),
                    User = new UserInfo
                    {
                        Id = user.Id,
                        Username = user.Username,
                        Email = user.Email,
                        Role = user.Role,
                        CreatedAt = user.CreatedAt,
                        LastLoginAt = user.LastLoginAt
                    }
                };

                _logger.LogInformation("用户 {Username} 登录成功", request.Username);
                return response;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "登录过程中发生错误：{Username}", request.Username);
                return null;
            }
        }

        /// <summary>
        /// 刷新访问令牌
        /// </summary>
        public async Task<TokenResponse?> RefreshTokenAsync(string refreshToken, string ipAddress)
        {
            try
            {
                var token = await _context.RefreshTokens
                    .Include(t => t.User)
                    .FirstOrDefaultAsync(t => t.Token == refreshToken);

                if (token == null || token.IsRevoked || token.ExpiresAt <= DateTime.UtcNow)
                {
                    _logger.LogWarning("刷新令牌无效或已过期");
                    return null;
                }

                if (!token.User.IsActive)
                {
                    _logger.LogWarning("用户账户已禁用：{Username}", token.User.Username);
                    return null;
                }

                // 生成新的访问令牌
                var newAccessToken = GenerateAccessToken(token.User);
                
                // 生成新的刷新令牌（可选：令牌轮转）
                var newRefreshToken = await GenerateRefreshTokenAsync(token.User, ipAddress, false);
                
                // 撤销旧的刷新令牌
                token.IsRevoked = true;
                token.RevokedAt = DateTime.UtcNow;
                token.RevokedByIp = ipAddress;

                await _context.SaveChangesAsync();

                return new TokenResponse
                {
                    AccessToken = newAccessToken,
                    RefreshToken = newRefreshToken.Token,
                    ExpiresIn = (int)_accessTokenLifetime.TotalSeconds,
                    ExpiresAt = DateTime.UtcNow.Add(_accessTokenLifetime)
                };
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "刷新令牌过程中发生错误");
                return null;
            }
        }

        /// <summary>
        /// 用户注册
        /// </summary>
        public async Task<bool> RegisterAsync(RegisterRequest request)
        {
            try
            {
                // 检查用户名是否已存在
                var existingUser = await _context.Users
                    .AnyAsync(u => u.Username == request.Username || u.Email == request.Email);

                if (existingUser)
                {
                    _logger.LogWarning("注册失败：用户名或邮箱已存在 - {Username}, {Email}", 
                        request.Username, request.Email);
                    return false;
                }

                var user = new User
                {
                    Username = request.Username,
                    Email = request.Email,
                    Role = UserRole.User, // 默认角色
                    LastPasswordChangeAt = DateTime.UtcNow
                };

                // 哈希密码
                user.PasswordHash = _passwordHasher.HashPassword(user, request.Password);

                _context.Users.Add(user);
                await _context.SaveChangesAsync();

                _logger.LogInformation("用户注册成功：{Username}", request.Username);
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "用户注册过程中发生错误：{Username}", request.Username);
                return false;
            }
        }

        /// <summary>
        /// 更改密码
        /// </summary>
        public async Task<bool> ChangePasswordAsync(Guid userId, ChangePasswordRequest request)
        {
            try
            {
                var user = await _context.Users.FindAsync(userId);
                if (user == null)
                {
                    return false;
                }

                // 验证当前密码
                var passwordVerificationResult = _passwordHasher.VerifyHashedPassword(user, user.PasswordHash, request.CurrentPassword);
                if (passwordVerificationResult != PasswordVerificationResult.Success)
                {
                    _logger.LogWarning("更改密码失败：当前密码错误 - {Username}", user.Username);
                    return false;
                }

                // 更新密码
                user.PasswordHash = _passwordHasher.HashPassword(user, request.NewPassword);
                user.LastPasswordChangeAt = DateTime.UtcNow;
                user.UpdatedAt = DateTime.UtcNow;

                // 撤销所有刷新令牌
                await RevokeAllTokensAsync(userId);

                await _context.SaveChangesAsync();

                _logger.LogInformation("用户 {Username} 密码更改成功", user.Username);
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "更改密码过程中发生错误：{UserId}", userId);
                return false;
            }
        }

        /// <summary>
        /// 撤销刷新令牌
        /// </summary>
        public async Task<bool> RevokeTokenAsync(string refreshToken, string ipAddress)
        {
            try
            {
                var token = await _context.RefreshTokens.FirstOrDefaultAsync(t => t.Token == refreshToken);
                if (token == null)
                {
                    return false;
                }

                token.IsRevoked = true;
                token.RevokedAt = DateTime.UtcNow;
                token.RevokedByIp = ipAddress;

                await _context.SaveChangesAsync();
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "撤销令牌过程中发生错误");
                return false;
            }
        }

        /// <summary>
        /// 撤销用户所有刷新令牌
        /// </summary>
        public async Task<bool> RevokeAllTokensAsync(Guid userId)
        {
            try
            {
                var tokens = await _context.RefreshTokens
                    .Where(t => t.UserId == userId && !t.IsRevoked)
                    .ToListAsync();

                foreach (var token in tokens)
                {
                    token.IsRevoked = true;
                    token.RevokedAt = DateTime.UtcNow;
                }

                await _context.SaveChangesAsync();
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "撤销用户所有令牌过程中发生错误：{UserId}", userId);
                return false;
            }
        }

        /// <summary>
        /// 获取用户信息
        /// </summary>
        public async Task<UserInfo?> GetUserInfoAsync(Guid userId)
        {
            try
            {
                var user = await _context.Users.FindAsync(userId);
                if (user == null || !user.IsActive)
                {
                    return null;
                }

                return new UserInfo
                {
                    Id = user.Id,
                    Username = user.Username,
                    Email = user.Email,
                    Role = user.Role,
                    CreatedAt = user.CreatedAt,
                    LastLoginAt = user.LastLoginAt
                };
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "获取用户信息过程中发生错误：{UserId}", userId);
                return null;
            }
        }

        /// <summary>
        /// 验证令牌
        /// </summary>
        public async Task<bool> ValidateTokenAsync(string token)
        {
            try
            {
                var tokenHandler = new JwtSecurityTokenHandler();
                var key = Encoding.ASCII.GetBytes(GetJwtSecretKey());

                var validationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    ValidIssuer = GetJwtIssuer(),
                    ValidAudience = GetJwtAudience(),
                    IssuerSigningKey = new SymmetricSecurityKey(key),
                    ClockSkew = TimeSpan.Zero
                };

                await tokenHandler.ValidateTokenAsync(token, validationParameters);
                return true;
            }
            catch
            {
                return false;
            }
        }

        #region Private Methods

        /// <summary>
        /// 检查账户是否被锁定
        /// </summary>
        private bool IsAccountLocked(User user)
        {
            return user.LockoutEndTime.HasValue && user.LockoutEndTime > DateTime.UtcNow;
        }

        /// <summary>
        /// 处理登录失败
        /// </summary>
        private async Task HandleFailedLoginAsync(User user, string ipAddress)
        {
            user.FailedLoginAttempts++;
            user.UpdatedAt = DateTime.UtcNow;

            if (user.FailedLoginAttempts >= _maxFailedAttempts)
            {
                user.LockoutEndTime = DateTime.UtcNow.Add(_lockoutDuration);
                _logger.LogWarning("账户 {Username} 因多次登录失败被锁定", user.Username);
            }

            await _context.SaveChangesAsync();
        }

        /// <summary>
        /// 处理登录成功
        /// </summary>
        private async Task HandleSuccessfulLoginAsync(User user)
        {
            user.FailedLoginAttempts = 0;
            user.LockoutEndTime = null;
            user.LastLoginAt = DateTime.UtcNow;
            user.UpdatedAt = DateTime.UtcNow;

            await _context.SaveChangesAsync();
        }

        /// <summary>
        /// 生成访问令牌
        /// </summary>
        private string GenerateAccessToken(User user)
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.ASCII.GetBytes(GetJwtSecretKey());

            var claims = new List<Claim>
            {
                new(ClaimTypes.NameIdentifier, user.Id.ToString()),
                new(ClaimTypes.Name, user.Username),
                new(ClaimTypes.Email, user.Email),
                new(ClaimTypes.Role, user.Role.ToString()),
                new("userId", user.Id.ToString()),
                new("username", user.Username),
                new("role", user.Role.ToString())
            };

            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(claims),
                Expires = DateTime.UtcNow.Add(_accessTokenLifetime),
                Issuer = GetJwtIssuer(),
                Audience = GetJwtAudience(),
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
            };

            var token = tokenHandler.CreateToken(tokenDescriptor);
            return tokenHandler.WriteToken(token);
        }

        /// <summary>
        /// 生成刷新令牌
        /// </summary>
        private async Task<RefreshToken> GenerateRefreshTokenAsync(User user, string ipAddress, bool rememberMe)
        {
            var tokenBytes = new byte[64];
            using var rng = RandomNumberGenerator.Create();
            rng.GetBytes(tokenBytes);

            var refreshToken = new RefreshToken
            {
                UserId = user.Id,
                Token = Convert.ToBase64String(tokenBytes),
                ExpiresAt = DateTime.UtcNow.Add(rememberMe ? TimeSpan.FromDays(30) : _refreshTokenLifetime),
                CreatedByIp = ipAddress
            };

            _context.RefreshTokens.Add(refreshToken);

            // 清理过期的刷新令牌
            await CleanupExpiredTokensAsync(user.Id);

            return refreshToken;
        }

        /// <summary>
        /// 清理过期的刷新令牌
        /// </summary>
        private async Task CleanupExpiredTokensAsync(Guid userId)
        {
            var expiredTokens = await _context.RefreshTokens
                .Where(t => t.UserId == userId && (t.ExpiresAt <= DateTime.UtcNow || t.IsRevoked))
                .ToListAsync();

            if (expiredTokens.Any())
            {
                _context.RefreshTokens.RemoveRange(expiredTokens);
            }
        }

        /// <summary>
        /// 获取JWT密钥
        /// </summary>
        private string GetJwtSecretKey()
        {
            return _configuration["JwtSettings:SecretKey"] 
                   ?? Environment.GetEnvironmentVariable("JWT_SECRET_KEY") 
                   ?? throw new InvalidOperationException("JWT Secret Key未配置");
        }

        /// <summary>
        /// 获取JWT发行者
        /// </summary>
        private string GetJwtIssuer()
        {
            return _configuration["JwtSettings:Issuer"] ?? "WeatherSystem";
        }

        /// <summary>
        /// 获取JWT受众
        /// </summary>
        private string GetJwtAudience()
        {
            return _configuration["JwtSettings:Audience"] ?? "WeatherSystemUsers";
        }

        #endregion
    }

    /// <summary>
    /// JWT令牌工具类
    /// </summary>
    public static class JwtHelper
    {
        /// <summary>
        /// 从JWT令牌中提取用户ID
        /// </summary>
        public static Guid? GetUserIdFromToken(string token)
        {
            try
            {
                var tokenHandler = new JwtSecurityTokenHandler();
                var jsonToken = tokenHandler.ReadJwtToken(token);
                
                var userIdClaim = jsonToken?.Claims?.FirstOrDefault(c => c.Type == "userId" || c.Type == ClaimTypes.NameIdentifier);
                
                if (userIdClaim != null && Guid.TryParse(userIdClaim.Value, out var userId))
                {
                    return userId;
                }

                return null;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// 从JWT令牌中提取用户角色
        /// </summary>
        public static UserRole? GetUserRoleFromToken(string token)
        {
            try
            {
                var tokenHandler = new JwtSecurityTokenHandler();
                var jsonToken = tokenHandler.ReadJwtToken(token);
                
                var roleClaim = jsonToken?.Claims?.FirstOrDefault(c => c.Type == "role" || c.Type == ClaimTypes.Role);
                
                if (roleClaim != null && Enum.TryParse<UserRole>(roleClaim.Value, out var role))
                {
                    return role;
                }

                return null;
            }
            catch
            {
                return null;
            }
        }
    }
}